﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using NDjango;
using NDjango.FiltersCS;
using System.Configuration;
using System.Reflection;
using MetaCollection.ContentService.Configuration;

namespace MetaCollection.ContentService.Template
{

	/// <summary>
	/// Singleton class which holds the instance of the Django template manager and Renders the templates.
	/// </summary>
	public static class TemplateManager
	{
		/// <summary>
		/// object to lock on during initialization
		/// </summary>
		private static object Lock = new object();
		/// <summary>
		/// A NDango manager that does most of the work.
		/// </summary>
		private static NDjango.Interfaces.ITemplateManager Manager;
		/// <summary>
		/// A collection of scripts that will be added to the rendering context of each template rendering request
		/// </summary>
		private static List<KeyValuePair<string, Uri>> IncludedScripts = new List<KeyValuePair<string, Uri>>();

		#region initialization and construction
		/// <summary>
		/// Initializes the <see cref="TemplateManager"/> class from configuration data.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification="We can't initialize our static fields from configuration data except in this constructor.")]
		static TemplateManager()
		{
			TemplateManagerProvider provider = new TemplateManagerProvider().WithLoader(new TemplateLoader());
			ContentConfigurationSection configSection = (ContentConfigurationSection)ConfigurationManager.GetSection("contentConfiguration");
			if (configSection != null)
			{
				LoadTemplateScripts(configSection);
				PopulateProviderTags(ref provider, configSection);
				PopulateProviderFilters(ref provider, configSection);
			}
			//provider = FilterManager.Initialize(provider);
			Manager = provider.GetNewManager();
		}

		/// <summary>
		/// Populates the provider tags.
		/// </summary>
		/// <param name="provider">The provider.</param>
		/// <param name="configSection">The config section.</param>
		private static void PopulateProviderTags(ref TemplateManagerProvider provider, ContentConfigurationSection configSection)
		{
			if (configSection == null)
				throw new ConfigurationErrorsException("contentConfiguration section not found in application configuration.");

			foreach (TemplateExtensionElement tag in configSection.Templating.Tags)
			{
				Assembly assembly = GetAssembly(tag.AssemblyName);
				Type tagClass = assembly.GetType(tag.TypeName, true);//assembly.GetTypes().FirstOrDefault(p => p.FullName == tag.Class || p.Name == tag.Class);
				provider = provider.WithTag(tag.Name, (NDjango.Interfaces.ITag)Activator.CreateInstance(tagClass));
			}
		}

		/// <summary>
		/// Populates the provider filters.
		/// </summary>
		/// <param name="provider">The provider.</param>
		/// <param name="configSection">The config section.</param>
		private static void PopulateProviderFilters(ref TemplateManagerProvider provider, ContentConfigurationSection configSection)
		{
			if (configSection == null)
				throw new ConfigurationErrorsException("contentConfiguration section not found in application configuration.");

			foreach (TemplateExtensionElement filter in configSection.Templating.Filters)
			{
				Assembly assembly = GetAssembly(filter.AssemblyName);
				Type filterClass = assembly.GetType(filter.TypeName, true);
				try
				{
					provider = provider.WithFilter(filter.Name, (NDjango.Interfaces.IFilter)Activator.CreateInstance(filterClass));
				}
				catch (InvalidCastException)
				{
					provider = provider.WithFilter(filter.Name, (NDjango.Interfaces.ISimpleFilter)Activator.CreateInstance(filterClass));
				}
			}
		}

		/// <summary>
		/// Loads the template scripts.
		/// </summary>
		/// <param name="configSection">The config section.</param>
		private static void LoadTemplateScripts(ContentConfigurationSection configSection)
		{
			if (IncludedScripts.Count < 1)
			{
				lock (Lock)
				{
					if (IncludedScripts.Count < 1)
					{
						foreach (ScriptElement script in configSection.Templating.Scripts)
						{
							IncludedScripts.Add(new KeyValuePair<string, Uri>(script.Name, script.Address));
						}
					}
				}
			}
		}
		/// <summary>
		/// Gets the assembly associated with a given assembly name.  If the name is null then the current assembly is returned.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <returns>an assembly identified by name</returns>
		private static Assembly GetAssembly(string name)
		{
			if (!String.IsNullOrEmpty(name))
				return Assembly.Load(name);
			else
				return Assembly.GetExecutingAssembly();
		}
		#endregion

		/// <summary>
		/// Renders the specified template.
		/// </summary>
		/// <param name="templatePath">The template path.</param>
		/// <param name="context">The template context.</param>
		/// <returns></returns>
		public static TextReader Render(string templatePath, Dictionary<string, object> context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			// add our scripts that need to be included in the output
			foreach (KeyValuePair<string, Uri> script in IncludedScripts)
			{
				context.Add(script.Key, script.Value);
			}

			return Manager.RenderTemplate(templatePath, context);
		}
	}

	
}
